﻿// Code adapted from 'The Mineserver Project'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Threading;
using NBT;

namespace ForgeCraft {
    public class RegionFile {
        // Adapted from https://github.com/fador/mineserver/blob/master/src/mcregion.cpp

        // TODO: Finish this, just can't get my head around it atm.
        World world;
        //public int x, z;

        private const int SectorBytes = 4096;
        private const int SectorInts = SectorBytes / 4;
        private const int ChunkHeaderSize = 5;

        private int[] offsets = new int[SectorInts];
        private int[] timestamps = new int[SectorInts];

        //File regionFile;
        int fileLength;

        #region Constructors

        public int sizeDelta, x, z;

        #endregion

        #region Loading

        public void OpenRegion(bool load, World world, int ChunkX, int ChunkZ) {

            int localX = (int)Math.Floor((double)ChunkX / 32);
            int localZ = (int)Math.Floor((double)ChunkZ / 32);

            Directory.CreateDirectory(world.name + "/region");
            
            if (File.Exists(RegionFilename(ChunkX, ChunkZ, world))) {
                using (FileStream fs = File.OpenRead(RegionFilename(ChunkX, ChunkZ, world)))
                {
                    //using (GZipStream gs = new GZipStream(fs, CompressionMode.Decompress))
                    //{
                        if (fs.Length < SectorBytes * 2)
                        {
                            for (int i = 0; i < SectorBytes * 2; i++)
                            {
                                fs.WriteByte(0);
                            }
                        }

                        if ((fs.Length & 0xfff) != 0)
                        {
                            for (int i = 0; i < (fs.Length & 0xfff); ++i)
                            {
                                fs.WriteByte(0);
                            }

                            fileLength = (int)fs.Length;
                        }

                        int sectorCount = fileLength / SectorBytes;

                        bool[] sectorFree = Enumerable.Range(2, sectorCount).Select(x => true).ToArray();
                        int[] timestamps = new int[SectorInts];

                        int offset = 0;
                        byte[] buffer = new byte[4];

                        //Read sectors and mark used
                        for (int i = 0; i < SectorInts; i++)
                        {
                            fs.Read(buffer, offset, offset >> 4);
                            offset = PacketWriter.SwapEndian(UInt32.Parse(buffer.ToString()));

                            offsets[i] = PacketWriter.SwapEndian(UInt32.Parse(buffer.ToString()));

                            if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.Length)
                            {
                                for (int sectorNum = 0; sectorNum < (offset & 0xFF); sectorNum++)
                                {
                                    sectorFree[(offset >> 8) + sectorNum] = false;
                                }
                            }

                            offset = offset >> 4;
                        }

                        offset = SectorInts;

                        //Read timestamps
                        for (int i = 0; i < SectorInts; i++)
                        {
                            fs.Read(buffer, offset, offset >> 4);

                            offset = PacketWriter.SwapEndian(UInt32.Parse(buffer.ToString()));

                            timestamps[i] = PacketWriter.SwapEndian((UInt32)fs.Read(buffer, offset, offset + 4));

                            offset = offset >> 4;
                        }
                }
            }
        }


        public bool writeChunk(byte chunkData, int dataLen, int x, int z)
        {

            return true;
        }

        public bool readChunk(byte chunkData, int dataLen, int x, int z)
        {

            return true;
        }

        #endregion

        #region Utils

        private static string RegionFilename(int x, int z, World world) {
            string tempX2 = ((int)Math.Floor((double)x / 32)).ToString();
            string tempZ2 = ((int)Math.Floor((double)z / 32)).ToString();

            if (!File.Exists(world.name + "/region/r." + tempX2 + "." + tempZ2 + ".mcr"))
            {
                File.Create(world.name + "/region/r." + tempX2 + "." + tempZ2 + ".mcr");
            }

            return world.name + "/region/r." + tempX2 + "." + tempZ2 + ".mcr";
        }

        /* is this an invalid chunk coordinate? */
        private bool outOfBounds(int x, int z)
        {
            return x < 0 || x >= 32 || z < 0 || z >= 32;
        }

        //Get chunk offset from the table
        private int getOffset(int x, int z)
        {
            return offsets[(x + z * 32)];
        }

        private bool hasChunk(int x, int z)
        {
            return getOffset(x, z) != 0;
        }

        //Set new offset for a chunk
        private void setOffset(int x, int z, int offset)
        {
            /*offsets[(x + z * 32)] = offset;
            fseek(regionFile, (x + z * 32) * 4, SEEK_SET);

            offset = htonl(offset);
            const char* tempBuf = (const char*)&offset;
            fwrite(tempBuf, 4, 1, regionFile);*/
        }

        //Set timestamp
        private void setTimestamp(int x, int z, int timestamp)
        {
            /*timestamps[(x + z * 32)] = timestamp;
            fseek(regionFile, SECTOR_BYTES + (x + z * 32) * 4, SEEK_SET);
            timestamp = htonl(timestamp);
            fwrite(reinterpret_cast<const char*>(&timestamp), 4, 1, regionFile);*/
        }
          

        // write a chunk data to the region file at specified sector number
        private void write(int sectorNumber, short data, int datalen)
        {
            /*fseek(regionFile, sectorNumber * SECTOR_BYTES, SEEK_SET);
            int chunklen = datalen + 1;
            chunklen = htonl(chunklen);
            fwrite(reinterpret_cast<const char*>(&chunklen), 4, 1, regionFile); // chunk length
            char version = VERSION_DEFLATE;
            fwrite(&version, 1, 1, regionFile); // chunk version number
            fwrite((char*)data, datalen, 1, regionFile); // chunk data*/
        }
          


        #endregion
    }
}
